前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# 数据加密 crypto

30 秒速记

  • 先分清要解决什么:保密用加密、防篡改用 HMAC / 签名、存密码用慢哈希、校验内容用摘要
  • 随机值用 crypto.randomBytes / crypto.randomUUID,Math.random 不能拿来生成 token、密钥
  • 密码存储用 scrypt / bcrypt / Argon2 加随机盐,不要直接 MD5 / SHA-256
  • 对称加密优先 AES-256-GCM 这类带认证的模式,每条消息一个新 IV
  • createCipher / createDecipher 已废弃,Node 22 删除,统一用 createCipheriv

crypto 模块是 Node 对 OpenSSL 的封装,用好它的前提是先想清楚你要的是保密、防篡改、证明身份还是存密码,这四件事用的工具完全不同。 摘要(createHash)只能看出内容变没变;HMAC 多了一把共享密钥,能证明是自己人生成的;数字签名用私钥签、公钥验,能证明是谁发的;加密(createCipheriv)才是让别人看不懂。存密码又是另一回事,要用 scrypt 这种故意算得很慢的函数。面试里最常见的错误回答就是把这几个混成「加密」,比如说「密码用 MD5 加密」,这句话本身就有两处错。

理解「数据加密 crypto」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 security 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

一张表记住 crypto 里几类能力分别解决什么问题:

需求 工具 Node 写法
判断内容有没有变 摘要 crypto.createHash('sha256')
证明消息是持有共享密钥的人发的 HMAC crypto.createHmac('sha256', key)
证明消息是某个私钥持有人发的 数字签名 crypto.sign / crypto.verify
让别人看不懂 对称加密 crypto.createCipheriv('aes-256-gcm', key, iv)
双方在公开信道上协商密钥 密钥交换 crypto.createECDH / crypto.diffieHellman
存用户密码 慢哈希 crypto.scrypt
生成随机 token / 密钥 安全随机数 crypto.randomBytes / crypto.randomUUID

一个能直接跑的密码存储例子:

const crypto = require('crypto');

function hashPassword(password) {
  const salt = crypto.randomBytes(16);
  const hash = crypto.scryptSync(password, salt, 64);
  return salt.toString('hex') + ':' + hash.toString('hex');
}

function checkPassword(password, stored) {
  const [saltHex, hashHex] = stored.split(':');
  const hash = crypto.scryptSync(password, Buffer.from(saltHex, 'hex'), 64);
  // 用 timingSafeEqual 比较,避免按字节提前退出带来的时序泄露
  return crypto.timingSafeEqual(hash, Buffer.from(hashHex, 'hex'));
}

const stored = hashPassword('123456');
console.log(checkPassword('123456', stored)); // true
console.log(checkPassword('654321', stored)); // false
@前端进阶之旅: 代码已经复制到剪贴板

记住两件事就不会出大错:密码不加密只做慢哈希,比较哈希值用 timingSafeEqual 不用 ===。

面试官追问

追问 1同事说「接口用了 crypto 模块,所以是安全的」,你怎么回?
参考回答

用了模块不代表用对了。要看具体调了什么:拿 MD5 存密码、AES 用固定 IV、用 Math.random 生成密钥,都是「用了 crypto」但一点都不安全。

追问 2生成重置密码的 token,Math.random().toString(36) 行不行?
参考回答

不行,Math.random 不是密码学安全的随机数,输出可以被预测。用 crypto.randomBytes(32).toString('hex'),或者 crypto.randomUUID()。

追问 3用户密码入库,sha256(password + salt) 够不够?
参考回答

不够。SHA-256 太快了,显卡一秒能算几十亿次,弱密码很快就被跑出来。用 crypto.scrypt(password, salt, 64, cb) 这类专门的慢哈希,盐每个用户随机生成一份。

追问 4加密结果要能防篡改,AES-CBC 加完就够了吗?
参考回答

不够,CBC 只管保密,密文被改了解出来就是乱码,不会报错。换成 aes-256-gcm,解密时校验 authTag,被改过直接抛异常;或者 CBC 后面再补一层 HMAC。

追问 5项目里搜出了 crypto.createCipher,要不要改?
参考回答

要改。它从 Node 10 起废弃,Node 22 已经删掉了,升级 Node 直接报错。而且它用 MD5 从密码推 key 和 IV,同一个密码每次 IV 都一样,换成 scrypt 派生 key 加 createCipheriv。

# 写在前面

30 秒速记

  • 这一章按「摘要 → HMAC → 加解密 → 签名 → 密钥交换 → 证书」的顺序走,难度递增
  • 每个 API 都看三件事:输入是什么编码、update 能不能多次调、final / digest 之后对象就作废
  • 示例里出现的 createCipher、createDecipher 是旧 API,Node 22 已删,只当历史了解
  • 版本差异看清楚:crypto.randomUUID 要 Node 14.17+,一次性 crypto.hash() 要 Node 21.7+ / 20.12+

这一章是 crypto 模块的使用笔记,读的时候抓住一条主线:每种能力都是「创建对象 → 多次 update 喂数据 → 一次 digest / final 收尾」。 哈希、HMAC、加密、签名全是这个套路,收尾之后对象就不能再用,要重新算就重新 create。另一个要带着看的是版本:文中有些示例写于 Node 6 时代,比如 createCipher,现在已经废弃并在 Node 22 删除,学原理没问题,写新代码要换成 createCipheriv。编码也要时刻留意,输入是 utf8 字符串还是 Buffer,输出要 hex 还是 base64,两端对不上是最常见的「算出来不一样」。

本章节写得差不多了,不过还需要再整理一下(TODO)。

理解「写在前面」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

面试官追问

追问 1hash、hmac、cipher、sign 这几个对象用法有什么共同点?
参考回答

都是 create 出来后 update 若干次,最后 digest / final / sign 一次收尾。收尾之后对象作废,再调会报错,而且它们都是 Transform 流,可以直接 pipe。

追问 2网上教程里的 crypto.createCipher('aes192', pwd) 能照着抄吗?
参考回答

别抄。这个 API 在 Node 22 已经没了,而且它推导出的 IV 每次都一样,不安全。新代码用 createCipheriv,key 用 scrypt 派生或者随机生成。

追问 3同一段文本,Node 和浏览器算出来的摘要不一样,先怀疑什么?
参考回答

先怀疑输入字节不一样,而不是算法。中文的编码、换行符 \r\n 和 \n、末尾有没有空格,任何一个字节不同摘要就完全不同。

追问 4示例想在 CI 里跑起来当回归测试,最少要做到什么?
参考回答

把输入固定成文件或常量,期望值写死在断言里,比如 sha256('hello') 的结果;出错路径要让进程非零退出。只截一张成功的终端截图没有任何约束力。

# hash例子

30 秒速记

  • 套路:createHash('sha256') → update() 可以调多次 → digest('hex') 收尾
  • digest 传 hex / base64 返回字符串,不传返回 Buffer
  • digest 之后对象作废,再 update / digest 抛 Digest already called
  • 大文件别 readFileSync,用流:
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
      • 写在前面
      • hash例子
      • HMAC例子
      • 加密/解密
        • crypto.createCipher(algorithm, password)
        • crypto.createDecipher(algorithm, password)
        • crypto.createCipheriv(algorithm, key, iv)
      • 数字签名/签名校验
      • DiffieHellman
      • ECDH:Elliptic Curve Diffie-Hellman
      • 证书
      • 关键点
      • 相关术语
      • 相关链接
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

数据加密 crypto|Nodejs专题系列

# 数据加密 crypto

30 秒速记

  • 先分清要解决什么:保密用加密、防篡改用 HMAC / 签名、存密码用慢哈希、校验内容用摘要
  • 随机值用 crypto.randomBytes / crypto.randomUUID,Math.random 不能拿来生成 token、密钥
  • 密码存储用 scrypt / bcrypt / Argon2 加随机盐,不要直接 MD5 / SHA-256
  • 对称加密优先 AES-256-GCM 这类带认证的模式,每条消息一个新 IV
  • createCipher / createDecipher 已废弃,Node 22 删除,统一用 createCipheriv

crypto 模块是 Node 对 OpenSSL 的封装,用好它的前提是先想清楚你要的是保密、防篡改、证明身份还是存密码,这四件事用的工具完全不同。 摘要(createHash)只能看出内容变没变;HMAC 多了一把共享密钥,能证明是自己人生成的;数字签名用私钥签、公钥验,能证明是谁发的;加密(createCipheriv)才是让别人看不懂。存密码又是另一回事,要用 scrypt 这种故意算得很慢的函数。面试里最常见的错误回答就是把这几个混成「加密」,比如说「密码用 MD5 加密」,这句话本身就有两处错。

理解「数据加密 crypto」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 security 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

一张表记住 crypto 里几类能力分别解决什么问题:

需求 工具 Node 写法
判断内容有没有变 摘要 crypto.createHash('sha256')
证明消息是持有共享密钥的人发的 HMAC crypto.createHmac('sha256', key)
证明消息是某个私钥持有人发的 数字签名 crypto.sign / crypto.verify
让别人看不懂 对称加密 crypto.createCipheriv('aes-256-gcm', key, iv)
双方在公开信道上协商密钥 密钥交换 crypto.createECDH / crypto.diffieHellman
存用户密码 慢哈希 crypto.scrypt
生成随机 token / 密钥 安全随机数 crypto.randomBytes / crypto.randomUUID

一个能直接跑的密码存储例子:

const crypto = require('crypto');

function hashPassword(password) {
  const salt = crypto.randomBytes(16);
  const hash = crypto.scryptSync(password, salt, 64);
  return salt.toString('hex') + ':' + hash.toString('hex');
}

function checkPassword(password, stored) {
  const [saltHex, hashHex] = stored.split(':');
  const hash = crypto.scryptSync(password, Buffer.from(saltHex, 'hex'), 64);
  // 用 timingSafeEqual 比较,避免按字节提前退出带来的时序泄露
  return crypto.timingSafeEqual(hash, Buffer.from(hashHex, 'hex'));
}

const stored = hashPassword('123456');
console.log(checkPassword('123456', stored)); // true
console.log(checkPassword('654321', stored)); // false
@前端进阶之旅: 代码已经复制到剪贴板

记住两件事就不会出大错:密码不加密只做慢哈希,比较哈希值用 timingSafeEqual 不用 ===。

面试官追问

追问 1同事说「接口用了 crypto 模块,所以是安全的」,你怎么回?
参考回答

用了模块不代表用对了。要看具体调了什么:拿 MD5 存密码、AES 用固定 IV、用 Math.random 生成密钥,都是「用了 crypto」但一点都不安全。

追问 2生成重置密码的 token,Math.random().toString(36) 行不行?
参考回答

不行,Math.random 不是密码学安全的随机数,输出可以被预测。用 crypto.randomBytes(32).toString('hex'),或者 crypto.randomUUID()。

追问 3用户密码入库,sha256(password + salt) 够不够?
参考回答

不够。SHA-256 太快了,显卡一秒能算几十亿次,弱密码很快就被跑出来。用 crypto.scrypt(password, salt, 64, cb) 这类专门的慢哈希,盐每个用户随机生成一份。

追问 4加密结果要能防篡改,AES-CBC 加完就够了吗?
参考回答

不够,CBC 只管保密,密文被改了解出来就是乱码,不会报错。换成 aes-256-gcm,解密时校验 authTag,被改过直接抛异常;或者 CBC 后面再补一层 HMAC。

追问 5项目里搜出了 crypto.createCipher,要不要改?
参考回答

要改。它从 Node 10 起废弃,Node 22 已经删掉了,升级 Node 直接报错。而且它用 MD5 从密码推 key 和 IV,同一个密码每次 IV 都一样,换成 scrypt 派生 key 加 createCipheriv。

# 写在前面

30 秒速记

  • 这一章按「摘要 → HMAC → 加解密 → 签名 → 密钥交换 → 证书」的顺序走,难度递增
  • 每个 API 都看三件事:输入是什么编码、update 能不能多次调、final / digest 之后对象就作废
  • 示例里出现的 createCipher、createDecipher 是旧 API,Node 22 已删,只当历史了解
  • 版本差异看清楚:crypto.randomUUID 要 Node 14.17+,一次性 crypto.hash() 要 Node 21.7+ / 20.12+

这一章是 crypto 模块的使用笔记,读的时候抓住一条主线:每种能力都是「创建对象 → 多次 update 喂数据 → 一次 digest / final 收尾」。 哈希、HMAC、加密、签名全是这个套路,收尾之后对象就不能再用,要重新算就重新 create。另一个要带着看的是版本:文中有些示例写于 Node 6 时代,比如 createCipher,现在已经废弃并在 Node 22 删除,学原理没问题,写新代码要换成 createCipheriv。编码也要时刻留意,输入是 utf8 字符串还是 Buffer,输出要 hex 还是 base64,两端对不上是最常见的「算出来不一样」。

本章节写得差不多了,不过还需要再整理一下(TODO)。

理解「写在前面」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

面试官追问

追问 1hash、hmac、cipher、sign 这几个对象用法有什么共同点?
参考回答

都是 create 出来后 update 若干次,最后 digest / final / sign 一次收尾。收尾之后对象作废,再调会报错,而且它们都是 Transform 流,可以直接 pipe。

追问 2网上教程里的 crypto.createCipher('aes192', pwd) 能照着抄吗?
参考回答

别抄。这个 API 在 Node 22 已经没了,而且它推导出的 IV 每次都一样,不安全。新代码用 createCipheriv,key 用 scrypt 派生或者随机生成。

追问 3同一段文本,Node 和浏览器算出来的摘要不一样,先怀疑什么?
参考回答

先怀疑输入字节不一样,而不是算法。中文的编码、换行符 \r\n 和 \n、末尾有没有空格,任何一个字节不同摘要就完全不同。

追问 4示例想在 CI 里跑起来当回归测试,最少要做到什么?
参考回答

把输入固定成文件或常量,期望值写死在断言里,比如 sha256('hello') 的结果;出错路径要让进程非零退出。只截一张成功的终端截图没有任何约束力。

# hash例子

30 秒速记

  • 套路:createHash('sha256') → update() 可以调多次 → digest('hex') 收尾
  • digest 传 hex / base64 返回字符串,不传返回 Buffer
  • digest 之后对象作废,再 update / digest 抛 Digest already called
  • 大文件别 readFileSync,用流:fs.createReadStream(f).pipe(hash),内存恒定
  • Node 21.7+ 有一次性 crypto.hash('sha256', data);要多次算就用 hash.copy()

算哈希就三步:createHash 选算法,update 喂数据,digest 拿结果。 update 可以分多次调,效果和把数据拼起来一次喂完全一样,所以处理分片上传、读流都很自然。digest 是一锤子买卖,调完对象就作废了,想拿到中间结果又继续算,要先 hash.copy() 复制一份。大文件一定用流,fs.createReadStream(file).pipe(hash),几个 GB 的文件内存也就占几十 KB;读文件时最好别指定 utf8,直接按原始字节算,否则二进制文件会被转坏。

← 9.2 实用工具模块 util9.4 MD5入门介绍及crypto模块的应用 →

fe